﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using FlatRedBall;
using FlatRedBall.Graphics;
using FlatRedBall.Graphics.Animation;
using FlatRedBall.Input;
using FlatRedBall.Math.Geometry;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;

using BulletHellBoxes.Utilities;
using System.Diagnostics;

namespace BulletHellBoxes.GameObjects.Player
{
    public class Attacker : GameObject
    {
        #region Constants
        private const float BASE_VELO = 5.0f;
        private const float SCALE = 0.25f;
        private const float START_X = -20.0f;
        private const float START_Y = 0.0f;
        private const int BUFFER_SIZE = 5;
        private const double DELAY = 0.1f;
        private const float COLLISION_MOD = 0.6f;

        private const float MAX_MULTI = 4.0f;
        private const float MIN_MULTI = 1.0f;
        #endregion

        #region Fields
        private Circle _collision;
        private double _lastBeat;
        private float[] _lastBeatBuffer;
        private float _averageBeat;
        private float _multiplier;
        private float _leeway;
        private int _lastIndex;
        private bool _triggerActive;
        private int _inactiveSeconds;
        #endregion

        #region Properties
        public Circle Collision
        {
            get { return _collision; }
        }
        #endregion

        #region Constructor
        public Attacker(GameController controller, int pNum)
        {
            PATH = @"Content/GameObjects/Attacker/" + pNum + "/";
            NUM_FRAMES = 22;
            FRAME_TIME = 0.06f;

            Initialize();
        }
        #endregion

        #region Initialization Methods
        protected override void Initialize()
        {
            base.Initialize();

            this.RelativePosition.X = START_X;
            this.RelativePosition.Y = START_Y;

            // Initialize Character Sprite
            _sprite.ScaleX *= SCALE;
            _sprite.ScaleY *= SCALE;

            _collision = ShapeManager.AddCircle();
            _collision.Radius = _sprite.ScaleX * COLLISION_MOD;
            _collision.AttachTo(this, false);
            _collision.Visible = false;

            _lastBeat = 0;
            _lastBeatBuffer = new float[BUFFER_SIZE];
            _lastIndex = -1;

            _averageBeat = 0.0f;
            _multiplier = 0.5f;
            _leeway = 1.0f;
            _inactiveSeconds = 1;

            _triggerActive = true;
        }

        #endregion

        #region Staple Methods
        public override void Activity()
        {
            throw new NotImplementedException();
        }

        public void Activity(ButtonState bs, Vector2 stickPos)
        {
            // Puts a minimum offset between trigger pulls
            if (_triggerActive)
            {
                // Detect a beat everytime the button is release.
                if (bs == ButtonState.Released)
                {
                    DetectBeat();
                    _inactiveSeconds = 1;
                }
                // Slow down the attacker if the beat stops.
                else if ((TimeManager.CurrentTime - _lastBeat) > (_averageBeat + (3 * _leeway)))
                {
                    double diff = (TimeManager.CurrentTime - _lastBeat) - _averageBeat - _leeway;

                    if (diff > _inactiveSeconds)
                    {
                        _inactiveSeconds++;
                        ScaleDown();
                    }
                }
            }
            // Waits before detecting another trigger pull
            else
            {
                if (TimeManager.CurrentTime - _lastBeat > DELAY)
                {
                    _triggerActive = true;
                }
            }

            RelativeVelocity.X = stickPos.X * BASE_VELO * _multiplier;
            RelativeVelocity.Y = stickPos.Y * BASE_VELO * _multiplier;

        }   

        public override void Destroy()
        {
            base.Destroy();
        
            ShapeManager.Remove(_collision);
        }

        public void Reset()
        {
            this.RelativeX = START_X;
            this.RelativeY = START_Y;

            for (int i = 0; i < BUFFER_SIZE; i++)
            {
                _lastBeatBuffer[i] = 0.0f;
            }
            _lastIndex = -1;
            _lastBeat = TimeManager.CurrentTime;

            _averageBeat = 0.0f;
            _multiplier = 0.5f;
            _leeway = 1.0f;

            // Initialize Character Sprite
            _sprite.PixelScale();
            _sprite.ScaleX *= SCALE;
            _sprite.ScaleY *= SCALE;

            _collision.Radius = _sprite.ScaleY * COLLISION_MOD;

            _inactiveSeconds = 1;
        }
        #endregion

        #region Private Methods
        private void DetectBeat()
        {

            double deltaBeat = TimeManager.CurrentTime - _lastBeat;
            _lastBeat = TimeManager.CurrentTime;

            if (_lastIndex == -1)
            {
                // Detect the first trigger pull of the game.
                _lastIndex++;
            }
            else if (_lastIndex == 0)
            {
                // Detect the first trigger pull sequence.
                SaveBeat((float)deltaBeat);
                CalculateAverage();
            }
            else if ((deltaBeat < _averageBeat + _leeway) && (deltaBeat > _averageBeat - _leeway))
            {
                ScaleUp();
                SaveBeat((float)deltaBeat);
                CalculateAverage();
            }
            else
            {
                // dynamically remove the multiplier
                //double avgDiff = Math.Abs(deltaBeat - (double)_averageBeat);
                //if (avgDiff > 0.5)
                //{
                //    avgDiff = 0.5f;
                //}
                ScaleDown();
                SaveBeat((float)deltaBeat);
                CalculateAverage();
            }

            _triggerActive = false;

        }

        private void SaveBeat(float deltaBeat)
        {
            // Keep track of total beats in _lastIndex.
            // Save the last BUFFER_SIZE in _lastBeatBuffer using the mod operator
            _lastBeatBuffer[_lastIndex % BUFFER_SIZE] = deltaBeat;
            _lastIndex++;
        }

        private void CalculateAverage()
        {
            // Calculate Average for the first BUFFER_SIZE elements
            if (_lastIndex < BUFFER_SIZE)
            {
                float rTotal = 0.0f;
                for (int i = 0; i < _lastIndex; i++)
                {
                    rTotal += _lastBeatBuffer[i];
                }

                _averageBeat = rTotal / _lastIndex;
            }
            // Calculate Averago for the subsequent elements.
            else
            {
                float rTotal = 0.0f;
                for (int i = 0; i < BUFFER_SIZE; i++)
                {
                    rTotal += _lastBeatBuffer[i];
                }

                _averageBeat = rTotal / BUFFER_SIZE;
            }

            _leeway = _averageBeat / 10;
            if (_leeway > .05f)
            {
                _leeway = .05f;
            }
        }

        private void ScaleDown()
        {
            // method used to slow down the attacker and scale him down.
            _multiplier -= .2f;
            if (_multiplier < MIN_MULTI)
            {
                _multiplier = MIN_MULTI;
            }
            else
            {
                _sprite.ScaleX -= 0.1f;
                _sprite.ScaleY -= 0.1f;
                if (_sprite.ScaleX < 1.0f)
                {
                    _sprite.ScaleX = 1.0f;
                    _sprite.ScaleY = 1.0f;
                    
                }
                _collision.Radius = _sprite.ScaleY * COLLISION_MOD;
            }
        }

        private void ScaleUp()
        {
            // method used to add to the modifier and scale him up
            _multiplier += 0.2f;

            if (_multiplier > MAX_MULTI)
            {
                _multiplier = MAX_MULTI;
            }
            else
            {
                _sprite.ScaleX += 0.1f;
                _sprite.ScaleY += 0.1f;
                if (_sprite.ScaleX > 2.0f)
                {
                    _sprite.ScaleX = 2.0f;
                    _sprite.ScaleY = 2.0f;
                }
                _collision.Radius = _sprite.ScaleY * COLLISION_MOD;
            }
        }
        #endregion
    }
}
